WIP: Add new AvoidDynamicallyCreatingVariableNames rule#2178
WIP: Add new AvoidDynamicallyCreatingVariableNames rule#2178iRon7 wants to merge 11 commits intoPowerShell:mainfrom
Conversation
|
👋 I get that this rule is warning against using New-\Set-Variable with a non-constant name. What's the issue with dynamic variable names? Could someone use the variable drive and I think I have one bit of code in an internal module that this rule would flag. It basically hoists a bunch of variables into a caller-visible scope. I think it's perfectly legitimate? The gist of it is: foreach ($name in $NamesToExpose) {
Set-Variable -Scope Script -Name $name -Value $data[$name]
}That aside... This misses alias-based creation and update calls. e.g. There's a helper function for this: PSScriptAnalyzer/Engine/Helper.cs Lines 216 to 233 in a143b9f And a sample usage here: PSScriptAnalyzer/Rules/AvoidGlobalAliases.cs Lines 95 to 96 in a143b9f You have the same copy-pasta in the class and I know it works as you're statically invoking parameter binding - but perhaps a test which uses positional binding |
|
Thank you for your feedback, |
Co-authored-by: Copilot <copilot@github.com>
There was a problem hiding this comment.
Pull request overview
Adds a new built-in PSScriptAnalyzer rule intended to discourage dynamically created variable names (favoring hashtables/dictionaries), along with documentation and Pester coverage.
Changes:
- Add new
AvoidDynamicallyCreatingVariableNamesrule implementation and localized strings. - Add rule documentation page and include it in the rules index.
- Add Pester tests covering violations, compliant cases, and suppression.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/Rules/README.md | Adds the new rule to the published rules list. |
| docs/Rules/AvoidDynamicallyCreatingVariableNames.md | Introduces end-user documentation and examples for the new rule. |
| Tests/Rules/AvoidDynamicallyCreatingVariableNames.tests.ps1 | Adds test coverage for rule detection and suppression behavior. |
| Rules/Strings.resx | Adds name/common name/description/error strings for the rule. |
| Rules/AvoidDynamicallyCreatingVariableNames.cs | Implements the new analyzer rule logic for detecting dynamic New-Variable -Name usage. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
It is meant as a best practice guidance for users that are not known with hash tables, as in e.g. https://stackoverflow.com/q/68827910/1701026. There are probably a lot of ways around it but I am not trying to completely close the door on dynamically creating variable names, but only want to prevent beginners from unknowingly choosing the wrong direction and possibly run into a pitfall. I don't think that the audience I am trying to reach with this rule will use something like
Yes, your example of
Implemented
Changed
Added And now Copilot also kicks in with suggestions... will I ever be able to finish this? 😆 |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <copilot@github.com>
There was a problem hiding this comment.
Thanks @iRon7, and thanks @liamjpeters for the deep review on this one — I think your back-and-forth genuinely improved the rule (narrowing scope to New-Variable, picking up the alias coverage via Helper.CmdletNameAndAliases, dropping severity to Information). All of Liam's points look addressed.
Two follow-ups before I'm comfortable merging:
Opt-in by default. This rule is still derived from IScriptRule, which means it's enabled-by-default for everyone. For a new rule — especially one at Information severity that is going to flag any New-Variable -Name $name in dynamic-config / metaprogramming-style scripts — our convention is to ship it opt-in via ConfigurableRule with Enable = false. Same recipe as AvoidExclaimOperator. Could you switch the base class and update docs/Rules/README.md accordingly?
Second maintainer read. Now that the scope is narrowed to just New-Variable, I'd like @bergmeister or @michaeltlombardi to weigh in on whether the rule still earns its keep at this scope, or whether it would be better recombined with the dynamic Set-Item Variable:\… form. Either answer is fine with me — just want a second pair of eyes on the design question before we land it.
Drafted by Copilot (Claude Opus 4.7)
This morning, I have also added an additional #2183 PR. Although, I see the value of preferring a Maybe a general solution (e.g. a setting like |
| Do not create variables with a dynamic name, this might introduce conflicts with | ||
| other variables and is difficult to maintain. |
There was a problem hiding this comment.
| Do not create variables with a dynamic name, this might introduce conflicts with | |
| other variables and is difficult to maintain. | |
| Don't create variables with dynamic names. It also makes the code difficult to understand and can | |
| lead to unexpected behavior if the variable names are not unique or if they collide with existing | |
| variables. A dynamic name is a name constructed using string concatenation or interpolation. | |
| This rule checks for the use of `New-Variable` with a dynamic name. |
|
|
||
| ## How | ||
|
|
||
| Use a hash table or similar dictionary type to store values with dynamic keys. |
There was a problem hiding this comment.
| Use a hash table or similar dictionary type to store values with dynamic keys. | |
| Use a hash table or similar dictionary type to store values with dynamic keys. When you require a | |
| specific scope, option, or visibility, put the dictionary (hashtable) in that scope and apply the | |
| appropriate option or visibility. |
| When a specific scope, option, or visibility is required, put the dictionary (hash table) in that | ||
| scope and apply the appropriate option or visibility. For example, if the values should be read-only and | ||
| available in the script scope, put the _hash table_ in the script scope and make it read-only. | ||
|
|
||
| ```powershell | ||
| New-Variable -Name My -Value @{} -Option ReadOnly -Scope Script | ||
| 'One', 'Two', 'Three' | ForEach-Object -Begin { $i = 1 } -Process { | ||
| $Script:My[$_] = $i++ | ||
| } | ||
| $Script:My.Two # returns 2 | ||
| ``` No newline at end of file |
There was a problem hiding this comment.
| When a specific scope, option, or visibility is required, put the dictionary (hash table) in that | |
| scope and apply the appropriate option or visibility. For example, if the values should be read-only and | |
| available in the script scope, put the _hash table_ in the script scope and make it read-only. | |
| ```powershell | |
| New-Variable -Name My -Value @{} -Option ReadOnly -Scope Script | |
| 'One', 'Two', 'Three' | ForEach-Object -Begin { $i = 1 } -Process { | |
| $Script:My[$_] = $i++ | |
| } | |
| $Script:My.Two # returns 2 | |
| ``` | |
| In this example, you want the values to be read-only and available in the script scope. Put the | |
| hashtable in the script scope and make it read-only. | |
| ```powershell | |
| New-Variable -Name My -Value @{} -Option ReadOnly -Scope Script | |
| 'One', 'Two', 'Three' | ForEach-Object -Begin { $i = 1 } -Process { | |
| $Script:My[$_] = $i++ | |
| } | |
| $Script:My.Two # returns 2 | |
| ``` | |
| ## References | |
| - [New-Variable](xref:Microsoft.PowerShell.Utility.New-Variable) | |
bergmeister
left a comment
There was a problem hiding this comment.
I am not sure we need a rule for this to warn when using New-Variable. PowerShell's own variable nature is dynamic and it can sometimes be a blessing and sometimes unpredictable and hard for PSSA. But it's up to user, there are far more dynamic things in PowerShell that allows users to shoot into their own foot
PR Summary
New rule to push for the use of hash tables rather than dynamically creating vriables in the general variable pool.
See also: https://stackoverflow.com/a/68830451/1701026
PR Checklist
.cs,.ps1and.psm1files have the correct copyright headerWIP:to the beginning of the title and remove the prefix when the PR is ready.